/*
 * jPhonePanel.java
 *
 * Created on Mar 22, 2010, 6:31:03 PM
 *
 * @author pquiring@gmail.com
 *
 */

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

import javaforce.*;
import javaforce.jni.JFNative;
import javaforce.voip.*;

/** jfPhone Panel */

public class PhonePanel extends BasePhone implements MeterController, GUI, VideoController {

  /** Creates new form jPhonePanel */
  public PhonePanel(WindowController wc, boolean isApplet) {
    try {
      initBasePhone(this, wc);
      Settings.isApplet = isApplet;
      initComponents();
      init();
      LocalCamera.init(lines);
      if (isApplet) {
        sidePanel.setVisible(false);
      }
      //must set focus to something or VK_BACK_SPACE beeps
      SwingUtilities.invokeLater(new Runnable(){
        public void run(){
          call.requestFocusInWindow();  //must be called from EDT or when window is visible
        }
      });
    } catch (Exception e) {
      JFLog.log("Error:" + e);
    }
  }

  /** This method is called from within the constructor to
   * initialize the form.
   * WARNING: Do NOT modify this code. The content of this method is
   * always regenerated by the Form Editor.
   */
  @SuppressWarnings("unchecked")
  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
  private void initComponents() {

    shareDesktopMenu = new javax.swing.JPopupMenu();
    shareDesktopViewOnly = new javax.swing.JMenuItem();
    shareDesktopRemoteControl = new javax.swing.JMenuItem();
    l1 = new javax.swing.JToggleButton();
    l2 = new javax.swing.JToggleButton();
    l3 = new javax.swing.JToggleButton();
    l4 = new javax.swing.JToggleButton();
    l5 = new javax.swing.JToggleButton();
    l6 = new javax.swing.JToggleButton();
    xfr = new javax.swing.JButton();
    hld = new javax.swing.JButton();
    aa = new javax.swing.JButton();
    ac = new javax.swing.JButton();
    dnd = new javax.swing.JButton();
    cnf = new javax.swing.JButton();
    call = new javax.swing.JToggleButton();
    end = new javax.swing.JToggleButton();
    n1 = new javax.swing.JButton();
    n2 = new javax.swing.JButton();
    n3 = new javax.swing.JButton();
    n4 = new javax.swing.JButton();
    n5 = new javax.swing.JButton();
    n6 = new javax.swing.JButton();
    n7 = new javax.swing.JButton();
    n8 = new javax.swing.JButton();
    n9 = new javax.swing.JButton();
    n0 = new javax.swing.JButton();
    nstar = new javax.swing.JButton();
    npound = new javax.swing.JButton();
    cfg = new javax.swing.JButton();
    clear = new javax.swing.JButton();
    redial = new javax.swing.JButton();
    status = new javax.swing.JTextField();
    dial = new javax.swing.JTextField();
    sidePanel = new javax.swing.JToggleButton();
    contactLabel = new javax.swing.JLabel();
    jScrollPane1 = new javax.swing.JScrollPane();
    contactJList = new javax.swing.JList();
    recentLabel = new javax.swing.JLabel();
    jScrollPane2 = new javax.swing.JScrollPane();
    recentList = new javax.swing.JList();
    delContact = new javax.swing.JButton();
    addContact = new javax.swing.JButton();
    editContact = new javax.swing.JButton();
    meterRec = new javax.swing.JProgressBar();
    meterPlay = new javax.swing.JProgressBar();
    mute = new javax.swing.JButton();
    headsetPic = new javax.swing.JLabel();
    micPic = new javax.swing.JLabel();
    backSpace = new javax.swing.JButton();
    micScale = new javax.swing.JLabel();
    spkScale = new javax.swing.JLabel();
    volRec = new javax.swing.JSlider();
    volPlay = new javax.swing.JSlider();
    presenceStatus = new javax.swing.JComboBox();
    jLabel1 = new javax.swing.JLabel();
    spk = new javax.swing.JButton();
    labels1 = new javax.swing.JLabel();
    spkPic = new javax.swing.JLabel();
    mutePic = new javax.swing.JLabel();
    labels2 = new javax.swing.JLabel();
    videoPanel = new javax.swing.JButton();
    fullScreenPic = new javax.swing.JLabel();
    enableVideoPic = new javax.swing.JLabel();
    videoPic = new javax.swing.JLabel();
    record = new javax.swing.JButton();
    recordPic = new javax.swing.JLabel();

    shareDesktopViewOnly.setText("Share Desktop (view only)");
    shareDesktopMenu.add(shareDesktopViewOnly);

    shareDesktopRemoteControl.setText("Share Desktop (remote control)");
    shareDesktopMenu.add(shareDesktopRemoteControl);

    setMaximumSize(new java.awt.Dimension(560, 350));
    setMinimumSize(new java.awt.Dimension(290, 350));
    setLayout(null);

    l1.setToolTipText("Line 1");
    l1.setMargin(new java.awt.Insets(2, 2, 2, 2));
    l1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        l1ActionPerformed(evt);
      }
    });
    add(l1);
    l1.setBounds(10, 70, 30, 30);

    l2.setToolTipText("Line 2");
    l2.setMargin(new java.awt.Insets(2, 2, 2, 2));
    l2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        l2ActionPerformed(evt);
      }
    });
    add(l2);
    l2.setBounds(50, 70, 30, 30);

    l3.setToolTipText("Line 3");
    l3.setMargin(new java.awt.Insets(2, 2, 2, 2));
    l3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        l3ActionPerformed(evt);
      }
    });
    add(l3);
    l3.setBounds(90, 70, 30, 30);

    l4.setToolTipText("Line 4");
    l4.setMargin(new java.awt.Insets(2, 2, 2, 2));
    l4.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        l4ActionPerformed(evt);
      }
    });
    add(l4);
    l4.setBounds(130, 70, 30, 30);

    l5.setToolTipText("Line 5");
    l5.setMargin(new java.awt.Insets(2, 2, 2, 2));
    l5.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        l5ActionPerformed(evt);
      }
    });
    add(l5);
    l5.setBounds(170, 70, 30, 30);

    l6.setToolTipText("Line 6");
    l6.setMargin(new java.awt.Insets(2, 2, 2, 2));
    l6.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        l6ActionPerformed(evt);
      }
    });
    add(l6);
    l6.setBounds(210, 70, 30, 30);

    xfr.setToolTipText("Transfer");
    xfr.setMargin(new java.awt.Insets(2, 0, 2, 0));
    xfr.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        xfrActionPerformed(evt);
      }
    });
    add(xfr);
    xfr.setBounds(10, 110, 30, 30);

    hld.setToolTipText("Hold");
    hld.setMargin(new java.awt.Insets(2, 0, 2, 0));
    hld.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        hldActionPerformed(evt);
      }
    });
    add(hld);
    hld.setBounds(50, 110, 30, 30);

    aa.setToolTipText("Auto Answer");
    aa.setMargin(new java.awt.Insets(2, 0, 2, 0));
    aa.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        aaActionPerformed(evt);
      }
    });
    add(aa);
    aa.setBounds(130, 110, 30, 30);

    ac.setToolTipText("Auto Conference");
    ac.setMargin(new java.awt.Insets(2, 0, 2, 0));
    ac.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        acActionPerformed(evt);
      }
    });
    add(ac);
    ac.setBounds(170, 110, 30, 30);

    dnd.setToolTipText("Do-Not-Disturb");
    dnd.setMargin(new java.awt.Insets(2, 0, 2, 0));
    dnd.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        dndActionPerformed(evt);
      }
    });
    add(dnd);
    dnd.setBounds(210, 110, 30, 30);

    cnf.setToolTipText("Conference");
    cnf.setMargin(new java.awt.Insets(2, 0, 2, 0));
    cnf.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        cnfActionPerformed(evt);
      }
    });
    add(cnf);
    cnf.setBounds(250, 110, 30, 30);

    call.setText("Call");
    call.setFocusCycleRoot(true);
    call.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        callActionPerformed(evt);
      }
    });
    add(call);
    call.setBounds(10, 150, 70, 50);

    end.setText("End");
    end.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        endActionPerformed(evt);
      }
    });
    add(end);
    end.setBounds(210, 150, 70, 50);

    n1.setText("1");
    n1.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n1.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n1ActionPerformed(evt);
      }
    });
    add(n1);
    n1.setBounds(90, 150, 30, 30);

    n2.setText("2");
    n2.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n2.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n2ActionPerformed(evt);
      }
    });
    add(n2);
    n2.setBounds(130, 150, 30, 30);

    n3.setText("3");
    n3.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n3.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n3ActionPerformed(evt);
      }
    });
    add(n3);
    n3.setBounds(170, 150, 30, 30);

    n4.setText("4");
    n4.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n4.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n4ActionPerformed(evt);
      }
    });
    add(n4);
    n4.setBounds(90, 190, 30, 30);

    n5.setText("5");
    n5.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n5.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n5ActionPerformed(evt);
      }
    });
    add(n5);
    n5.setBounds(130, 190, 30, 30);

    n6.setText("6");
    n6.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n6.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n6ActionPerformed(evt);
      }
    });
    add(n6);
    n6.setBounds(170, 190, 30, 30);

    n7.setText("7");
    n7.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n7.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n7ActionPerformed(evt);
      }
    });
    add(n7);
    n7.setBounds(90, 230, 30, 30);

    n8.setText("8");
    n8.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n8.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n8ActionPerformed(evt);
      }
    });
    add(n8);
    n8.setBounds(130, 230, 30, 30);

    n9.setText("9");
    n9.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n9.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n9ActionPerformed(evt);
      }
    });
    add(n9);
    n9.setBounds(170, 230, 30, 30);

    n0.setText("0");
    n0.setMargin(new java.awt.Insets(2, 2, 2, 2));
    n0.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        n0ActionPerformed(evt);
      }
    });
    add(n0);
    n0.setBounds(130, 270, 30, 30);

    nstar.setText("*");
    nstar.setMargin(new java.awt.Insets(2, 2, 2, 2));
    nstar.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        nstarActionPerformed(evt);
      }
    });
    add(nstar);
    nstar.setBounds(90, 270, 30, 30);

    npound.setText("#");
    npound.setMargin(new java.awt.Insets(2, 2, 2, 2));
    npound.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        npoundActionPerformed(evt);
      }
    });
    add(npound);
    npound.setBounds(170, 270, 30, 30);

    cfg.setToolTipText("Configure");
    cfg.setMargin(new java.awt.Insets(2, 0, 2, 0));
    cfg.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        cfgActionPerformed(evt);
      }
    });
    add(cfg);
    cfg.setBounds(250, 70, 30, 30);

    clear.setText("CLR");
    clear.setToolTipText("Clear");
    clear.setMargin(new java.awt.Insets(2, 0, 2, 0));
    clear.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        clearActionPerformed(evt);
      }
    });
    add(clear);
    clear.setBounds(170, 310, 30, 30);

    redial.setToolTipText("Redial");
    redial.setMargin(new java.awt.Insets(2, 0, 2, 0));
    redial.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        redialActionPerformed(evt);
      }
    });
    add(redial);
    redial.setBounds(90, 110, 30, 30);

    status.setEditable(false);
    add(status);
    status.setBounds(10, 40, 240, 20);

    dial.setEditable(false);
    add(dial);
    dial.setBounds(10, 10, 240, 20);

    sidePanel.setText(">");
    sidePanel.setToolTipText("Toggle Side Panel");
    sidePanel.setMargin(new java.awt.Insets(2, 0, 2, 0));
    sidePanel.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        sidePanelActionPerformed(evt);
      }
    });
    add(sidePanel);
    sidePanel.setBounds(260, 10, 20, 50);

    contactLabel.setText("Contacts");
    add(contactLabel);
    contactLabel.setBounds(290, 10, 70, 10);

    contactJList.setModel(new javax.swing.AbstractListModel() {
      String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
      public int getSize() { return strings.length; }
      public Object getElementAt(int i) { return strings[i]; }
    });
    contactJList.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseClicked(java.awt.event.MouseEvent evt) {
        contactJListMouseClicked(evt);
      }
    });
    contactJList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
      public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
        contactJListValueChanged(evt);
      }
    });
    jScrollPane1.setViewportView(contactJList);

    add(jScrollPane1);
    jScrollPane1.setBounds(290, 30, 250, 150);

    recentLabel.setText("Recent Calls");
    add(recentLabel);
    recentLabel.setBounds(290, 190, 80, 10);

    recentList.setModel(new javax.swing.AbstractListModel() {
      String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
      public int getSize() { return strings.length; }
      public Object getElementAt(int i) { return strings[i]; }
    });
    recentList.addMouseListener(new java.awt.event.MouseAdapter() {
      public void mouseClicked(java.awt.event.MouseEvent evt) {
        recentListMouseClicked(evt);
      }
    });
    recentList.addListSelectionListener(new javax.swing.event.ListSelectionListener() {
      public void valueChanged(javax.swing.event.ListSelectionEvent evt) {
        recentListValueChanged(evt);
      }
    });
    jScrollPane2.setViewportView(recentList);

    add(jScrollPane2);
    jScrollPane2.setBounds(290, 210, 250, 130);

    delContact.setText("Remove");
    delContact.setMargin(new java.awt.Insets(2, 0, 2, 0));
    delContact.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        delContactActionPerformed(evt);
      }
    });
    add(delContact);
    delContact.setBounds(480, 10, 60, 20);

    addContact.setText("Add");
    addContact.setMargin(new java.awt.Insets(2, 0, 2, 0));
    addContact.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        addContactActionPerformed(evt);
      }
    });
    add(addContact);
    addContact.setBounds(360, 10, 50, 20);

    editContact.setText("Edit");
    editContact.setMargin(new java.awt.Insets(2, 0, 2, 0));
    editContact.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        editContactActionPerformed(evt);
      }
    });
    add(editContact);
    editContact.setBounds(420, 10, 50, 20);

    meterRec.setOrientation(1);
    add(meterRec);
    meterRec.setBounds(40, 210, 10, 90);

    meterPlay.setOrientation(1);
    add(meterPlay);
    meterPlay.setBounds(270, 210, 10, 90);

    mute.setToolTipText("Mute");
    mute.setMargin(new java.awt.Insets(2, 0, 2, 0));
    mute.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        muteActionPerformed(evt);
      }
    });
    add(mute);
    mute.setBounds(10, 310, 30, 30);

    headsetPic.setText(".");
    add(headsetPic);
    headsetPic.setBounds(210, 230, 30, 30);

    micPic.setText(".");
    add(micPic);
    micPic.setBounds(50, 230, 30, 30);

    backSpace.setText("<");
    backSpace.setMargin(new java.awt.Insets(2, 2, 2, 2));
    backSpace.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        backSpaceActionPerformed(evt);
      }
    });
    add(backSpace);
    backSpace.setBounds(90, 310, 30, 30);

    micScale.setText(".");
    add(micScale);
    micScale.setBounds(30, 210, 10, 90);

    spkScale.setText(".");
    add(spkScale);
    spkScale.setBounds(260, 210, 10, 90);

    volRec.setOrientation(javax.swing.JSlider.VERTICAL);
    volRec.setValue(75);
    volRec.addChangeListener(new javax.swing.event.ChangeListener() {
      public void stateChanged(javax.swing.event.ChangeEvent evt) {
        volRecStateChanged(evt);
      }
    });
    add(volRec);
    volRec.setBounds(10, 210, 20, 90);

    volPlay.setOrientation(javax.swing.JSlider.VERTICAL);
    volPlay.setValue(75);
    volPlay.addChangeListener(new javax.swing.event.ChangeListener() {
      public void stateChanged(javax.swing.event.ChangeEvent evt) {
        volPlayStateChanged(evt);
      }
    });
    add(volPlay);
    volPlay.setBounds(240, 210, 20, 90);

    presenceStatus.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Auto", "DND", "Busy", "Idle", "Offline" }));
    presenceStatus.setEnabled(false);
    presenceStatus.addItemListener(new java.awt.event.ItemListener() {
      public void itemStateChanged(java.awt.event.ItemEvent evt) {
        presenceStatusItemStateChanged(evt);
      }
    });
    add(presenceStatus);
    presenceStatus.setBounds(450, 185, 90, 20);

    jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.TRAILING);
    jLabel1.setText("Status:");
    add(jLabel1);
    jLabel1.setBounds(380, 190, 70, 10);

    spk.setToolTipText("Speaker Phone Mode");
    spk.setMargin(new java.awt.Insets(2, 0, 2, 0));
    spk.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        spkActionPerformed(evt);
      }
    });
    add(spk);
    spk.setBounds(250, 310, 30, 30);
    add(labels1);
    labels1.setBounds(10, 100, 270, 10);
    add(spkPic);
    spkPic.setBounds(250, 340, 30, 10);
    add(mutePic);
    mutePic.setBounds(10, 340, 30, 10);
    add(labels2);
    labels2.setBounds(10, 140, 270, 10);

    videoPanel.setToolTipText("Show Video Panel");
    videoPanel.setMargin(new java.awt.Insets(2, 0, 2, 0));
    videoPanel.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        videoPanelActionPerformed(evt);
      }
    });
    add(videoPanel);
    videoPanel.setBounds(210, 310, 30, 30);
    add(fullScreenPic);
    fullScreenPic.setBounds(470, 670, 60, 10);
    add(enableVideoPic);
    enableVideoPic.setBounds(20, 670, 60, 10);
    add(videoPic);
    videoPic.setBounds(210, 340, 30, 10);

    record.setToolTipText("Record");
    record.setMargin(new java.awt.Insets(2, 0, 2, 0));
    record.addActionListener(new java.awt.event.ActionListener() {
      public void actionPerformed(java.awt.event.ActionEvent evt) {
        recordActionPerformed(evt);
      }
    });
    add(record);
    record.setBounds(50, 310, 30, 30);
    add(recordPic);
    recordPic.setBounds(50, 340, 30, 10);
  }// </editor-fold>//GEN-END:initComponents

    private void n3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n3ActionPerformed
      addDigit('3');
    }//GEN-LAST:event_n3ActionPerformed

    private void cfgActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cfgActionPerformed
      if (registeringAll) {
        doConfig = true;  //do after registerAll is complete
        return;
      }
      for(int a=0;a<6;a++) { if (lines[a].incall) return; }
      cfg.setIcon(ii[PIC_GREEN]);
      unRegisterAll();
      selectLine(-1);
      for(int a=0;a<6;a++) { lines[a].msgwaiting = false; }
      sound.uninit();
      dial.setText("");
      status.setText("");
      EditSettings.editSettings(null);
      initDTLS();
      reset();
      cfg.setIcon(ii[PIC_GREY]);
    }//GEN-LAST:event_cfgActionPerformed

    private void n1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n1ActionPerformed
      addDigit('1');
    }//GEN-LAST:event_n1ActionPerformed

    private void n2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n2ActionPerformed
      addDigit('2');
    }//GEN-LAST:event_n2ActionPerformed

    private void n4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n4ActionPerformed
      addDigit('4');
    }//GEN-LAST:event_n4ActionPerformed

    private void n5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n5ActionPerformed
      addDigit('5');
    }//GEN-LAST:event_n5ActionPerformed

    private void n6ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n6ActionPerformed
      addDigit('6');
    }//GEN-LAST:event_n6ActionPerformed

    private void n7ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n7ActionPerformed
      addDigit('7');
    }//GEN-LAST:event_n7ActionPerformed

    private void n8ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n8ActionPerformed
      addDigit('8');
    }//GEN-LAST:event_n8ActionPerformed

    private void n9ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n9ActionPerformed
      addDigit('9');
    }//GEN-LAST:event_n9ActionPerformed

    private void nstarActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nstarActionPerformed
      addDigit('*');
    }//GEN-LAST:event_nstarActionPerformed

    private void n0ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_n0ActionPerformed
      addDigit('0');
    }//GEN-LAST:event_n0ActionPerformed

    private void npoundActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_npoundActionPerformed
      addDigit('#');
    }//GEN-LAST:event_npoundActionPerformed

    private void clearActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearActionPerformed
      clear();
    }//GEN-LAST:event_clearActionPerformed

    private void callActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_callActionPerformed
      call();
    }//GEN-LAST:event_callActionPerformed

    private void endActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_endActionPerformed
      end();
    }//GEN-LAST:event_endActionPerformed

    private void l1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_l1ActionPerformed
      selectLine(0);
    }//GEN-LAST:event_l1ActionPerformed

    private void l2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_l2ActionPerformed
      selectLine(1);
    }//GEN-LAST:event_l2ActionPerformed

    private void l3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_l3ActionPerformed
      selectLine(2);
    }//GEN-LAST:event_l3ActionPerformed

    private void l4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_l4ActionPerformed
      selectLine(3);
    }//GEN-LAST:event_l4ActionPerformed

    private void l5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_l5ActionPerformed
      selectLine(4);
    }//GEN-LAST:event_l5ActionPerformed

    private void l6ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_l6ActionPerformed
      selectLine(5);
    }//GEN-LAST:event_l6ActionPerformed

    private void xfrActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_xfrActionPerformed
      doXfer();
    }//GEN-LAST:event_xfrActionPerformed

    private void hldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_hldActionPerformed
      doHold();
    }//GEN-LAST:event_hldActionPerformed

    private void redialActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_redialActionPerformed
      doRedial();
    }//GEN-LAST:event_redialActionPerformed

    private void aaActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aaActionPerformed
      toggleAA();
    }//GEN-LAST:event_aaActionPerformed

    private void acActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_acActionPerformed
      toggleAC();
    }//GEN-LAST:event_acActionPerformed

    private void dndActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dndActionPerformed
      toggleDND();
    }//GEN-LAST:event_dndActionPerformed

    private void cnfActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cnfActionPerformed
      doConference();
    }//GEN-LAST:event_cnfActionPerformed

    private void sidePanelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sidePanelActionPerformed
      toggleContacts();
    }//GEN-LAST:event_sidePanelActionPerformed

    private void addContactActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addContactActionPerformed
      addContact();
    }//GEN-LAST:event_addContactActionPerformed

    private void delContactActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_delContactActionPerformed
      delContact();
    }//GEN-LAST:event_delContactActionPerformed

    private void contactJListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_contactJListValueChanged
//      dialContact();
    }//GEN-LAST:event_contactJListValueChanged

    private void recentListValueChanged(javax.swing.event.ListSelectionEvent evt) {//GEN-FIRST:event_recentListValueChanged
//      dialRecent();
    }//GEN-LAST:event_recentListValueChanged

    private void contactJListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_contactJListMouseClicked
      if (evt.getClickCount() == 2) dialContact();
    }//GEN-LAST:event_contactJListMouseClicked

    private void recentListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_recentListMouseClicked
      if (evt.getClickCount() == 2) dialRecent();
    }//GEN-LAST:event_recentListMouseClicked

    private void editContactActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editContactActionPerformed
      editContact();
    }//GEN-LAST:event_editContactActionPerformed

    private void muteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_muteActionPerformed
      toggleMute();
    }//GEN-LAST:event_muteActionPerformed

    private void backSpaceActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_backSpaceActionPerformed
      addDigit((char)KeyEvent.VK_BACK_SPACE);
    }//GEN-LAST:event_backSpaceActionPerformed

    private void volRecStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_volRecStateChanged
      setVolRec();
    }//GEN-LAST:event_volRecStateChanged

    private void volPlayStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_volPlayStateChanged
      setVolPlay();
    }//GEN-LAST:event_volPlayStateChanged

    private void presenceStatusItemStateChanged(java.awt.event.ItemEvent evt) {//GEN-FIRST:event_presenceStatusItemStateChanged
      publishPresence();
    }//GEN-LAST:event_presenceStatusItemStateChanged

    private void spkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_spkActionPerformed
      toggleSpeaker();
    }//GEN-LAST:event_spkActionPerformed

  private void videoPanelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_videoPanelActionPerformed
    toggleVideoPanel(line);
  }//GEN-LAST:event_videoPanelActionPerformed

  private void recordActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_recordActionPerformed
    toggleRecord();
  }//GEN-LAST:event_recordActionPerformed

  // Variables declaration - do not modify//GEN-BEGIN:variables
  private javax.swing.JButton aa;
  private javax.swing.JButton ac;
  private javax.swing.JButton addContact;
  private javax.swing.JButton backSpace;
  private javax.swing.JToggleButton call;
  private javax.swing.JButton cfg;
  private javax.swing.JButton clear;
  private javax.swing.JButton cnf;
  private javax.swing.JList contactJList;
  private javax.swing.JLabel contactLabel;
  private javax.swing.JButton delContact;
  private javax.swing.JTextField dial;
  private javax.swing.JButton dnd;
  private javax.swing.JButton editContact;
  private javax.swing.JLabel enableVideoPic;
  private javax.swing.JToggleButton end;
  private javax.swing.JLabel fullScreenPic;
  private javax.swing.JLabel headsetPic;
  private javax.swing.JButton hld;
  private javax.swing.JLabel jLabel1;
  private javax.swing.JScrollPane jScrollPane1;
  private javax.swing.JScrollPane jScrollPane2;
  private javax.swing.JToggleButton l1;
  private javax.swing.JToggleButton l2;
  private javax.swing.JToggleButton l3;
  private javax.swing.JToggleButton l4;
  private javax.swing.JToggleButton l5;
  private javax.swing.JToggleButton l6;
  private javax.swing.JLabel labels1;
  private javax.swing.JLabel labels2;
  private javax.swing.JProgressBar meterPlay;
  private javax.swing.JProgressBar meterRec;
  private javax.swing.JLabel micPic;
  private javax.swing.JLabel micScale;
  private javax.swing.JButton mute;
  private javax.swing.JLabel mutePic;
  private javax.swing.JButton n0;
  private javax.swing.JButton n1;
  private javax.swing.JButton n2;
  private javax.swing.JButton n3;
  private javax.swing.JButton n4;
  private javax.swing.JButton n5;
  private javax.swing.JButton n6;
  private javax.swing.JButton n7;
  private javax.swing.JButton n8;
  private javax.swing.JButton n9;
  private javax.swing.JButton npound;
  private javax.swing.JButton nstar;
  private javax.swing.JComboBox presenceStatus;
  private javax.swing.JLabel recentLabel;
  private javax.swing.JList recentList;
  private javax.swing.JButton record;
  private javax.swing.JLabel recordPic;
  private javax.swing.JButton redial;
  private javax.swing.JPopupMenu shareDesktopMenu;
  private javax.swing.JMenuItem shareDesktopRemoteControl;
  private javax.swing.JMenuItem shareDesktopViewOnly;
  private javax.swing.JToggleButton sidePanel;
  private javax.swing.JButton spk;
  private javax.swing.JLabel spkPic;
  private javax.swing.JLabel spkScale;
  private javax.swing.JTextField status;
  private javax.swing.JButton videoPanel;
  private javax.swing.JLabel videoPic;
  private javax.swing.JSlider volPlay;
  private javax.swing.JSlider volRec;
  private javax.swing.JButton xfr;
  // End of variables declaration//GEN-END:variables

  /** Init panel variables. */

  public void init() {
    JFLog.log("jfphone/" + version);
    loadIcons();
    Settings.loadSettings();
    if (Settings.current.welcome) {
      new Thread() {
        public void run() {
        java.awt.EventQueue.invokeLater(new Runnable() {
          public void run() {
            JFAWT.showMessage("Welcome", "Welcome to jfPhone!\nPlease press CFG button to configure your options.");
          }
        });
        }
      }.start();
      Settings.current.welcome = false;
      Settings.saveSettings();
    }

    contactJList.setCellRenderer(new ListCellRenderer() {
      public Component getListCellRendererComponent(
        JList list,              // the list
        Object value,            // value to display
        int index,               // cell index
        boolean isSelected,      // is the cell selected
        boolean cellHasFocus)    // does the cell have focus
      {
        //each Object 'is' a JLabel (Contact) which makes this easy
        JLabel label = (JLabel)value;
        if (isSelected) {
          label.setBackground(list.getSelectionBackground());
          label.setForeground(list.getSelectionForeground());
        } else {
          label.setBackground(list.getBackground());
          label.setForeground(list.getForeground());
        }
        label.setEnabled(list.isEnabled());
        label.setFont(list.getFont());
        label.setOpaque(true);
        return label;
     }
    });

    if (!Settings.isApplet) {
      if (SystemTray.isSupported()) {
        tray = SystemTray.getSystemTray();
        // create a popup menu
        PopupMenu popup = new PopupMenu();
        show = new MenuItem("Show");
        show.addActionListener(this);
        popup.add(show);
        popup.addSeparator();
        exit = new MenuItem("Exit");
        exit.addActionListener(this);
        popup.add(exit);
        Dimension size = tray.getTrayIconSize();
        ImageIcon appicon = ii[PIC_TRAY];
        JFImage scaled = new JFImage(size.width, size.height);
        scaled.fill(0, 0, size.width, size.height, 0x00000000, true);  //fill with alpha transparent
        if (false) {
          //scaled image (looks bad sometimes)
          scaled.getGraphics().drawImage(appicon.getImage()
            , 0, 0, size.width, size.height
            , 0, 0, appicon.getIconWidth(), appicon.getIconHeight()
            , null);
        } else {
          //center image
          scaled.getGraphics().drawImage(appicon.getImage()
            , (size.width - appicon.getIconWidth()) / 2
            , (size.height - appicon.getIconHeight()) / 2
            , null);
        }
        icon = new TrayIcon(scaled.getImage(), "jfPhone", popup);
        icon.addActionListener(this);
        try { tray.add(icon); } catch (Exception e) { JFLog.log(e); }
      }
    }
    lines = new PhoneLine[6];
    lineButtons = new JToggleButton[6];
    lineButtons[0] = l1;
    lineButtons[1] = l2;
    lineButtons[2] = l3;
    lineButtons[3] = l4;
    lineButtons[4] = l5;
    lineButtons[5] = l6;
    for(int a=0;a<6;a++) {
      lineButtons[a].setMargin(new Insets(0,0,0,0));
      lineButtons[a].setIconTextGap(0);
      lines[a] = new PhoneLine();
    }
    numButtons = new JButton[12];
    numButtons[0] = n1;
    numButtons[1] = n2;
    numButtons[2] = n3;
    numButtons[3] = n4;
    numButtons[4] = n5;
    numButtons[5] = n6;
    numButtons[6] = n7;
    numButtons[7] = n8;
    numButtons[8] = n9;
    numButtons[9] = n0;
    numButtons[10] = nstar;
    numButtons[11] = npound;
    for(int a=0;a<12;a++) {
      numButtons[a].addMouseListener(new MouseAdapter() {
        public void mousePressed(MouseEvent e) {
          JButton o = (JButton)e.getSource();
          pressDigit(o.getText().charAt(0));
        }
        public void mouseReleased(MouseEvent e) {
          JButton o = (JButton)e.getSource();
          releaseDigit(o.getText().charAt(0));
        }
      });
    }

    //set PICs

    call.setText("");
    call.setIcon(ii[PIC_CALL]);
//    call.setPressedIcon(ii[PIC_CALL2]);
    call.setSelectedIcon(ii[PIC_CALL2]);

    end.setText("");
    end.setIcon(ii[PIC_END]);
//    end.setPressedIcon(ii[PIC_END2]);
    end.setSelectedIcon(ii[PIC_END2]);

    labels1.setText("");
    labels1.setIcon(ii[PIC_LABELS1]);

    labels2.setText("");
    labels2.setIcon(ii[PIC_LABELS2]);

    micPic.setText("");
    micPic.setIcon(ii[PIC_MIC]);
    headsetPic.setText("");
    headsetPic.setIcon(ii[PIC_HEADSET]);

    spk.setText("");
    spk.setIcon(ii[Settings.current.speakerMode ? PIC_GREEN : PIC_GREY]);
    spkPic.setText("");
    spkPic.setIcon(ii[PIC_SPK]);

    videoPanel.setIcon(ii[PIC_GREY]);
    videoPic.setText("");
    videoPic.setIcon(ii[PIC_VIDEO]);

    mute.setText("");
    mute.setIcon(ii[PIC_GREY]);
    mutePic.setText("");
    mutePic.setIcon(ii[PIC_MUTE]);

    xfr.setText("");
    xfr.setIcon(ii[PIC_GREY]);
    hld.setText("");
    hld.setIcon(ii[PIC_GREY]);
    redial.setText("");
    redial.setIcon(ii[PIC_GREY]);
    aa.setText("");
    aa.setIcon(ii[PIC_GREY]);
    ac.setText("");
    ac.setIcon(ii[PIC_GREY]);
    dnd.setText("");
    dnd.setIcon(ii[PIC_GREY]);
    cnf.setText("");
    cnf.setIcon(ii[PIC_GREY]);
    cfg.setText("");
    cfg.setIcon(ii[PIC_GREY]);

    record.setIcon(ii[PIC_GREY]);
    recordPic.setText("");
    recordPic.setIcon(ii[PIC_RECORD]);

    java.util.Timer buttontimer = new java.util.Timer();
    buttontimer.schedule(new ButtonColorUpdater(), 0, 250);

    keepAliveinit();
    reset();
    if (Settings.current.checkVersion) {
      new Thread() {
        public void run() {
          checkVersion();
       }
      }.start();
    }
  }

  /** MeterController : Sets the recording level on the display meter. */

  public void setMeterRec(int lvl) {meterRec.setValue(lvl);}

  /** MeterController : Sets the playback level on the display meter. */

  public void setMeterPlay(int lvl) {meterPlay.setValue(lvl);}

  /** MeterController : Sets the speaker mic status (green=true red=false). */

  public void setSpeakerStatus(boolean state) {
    if (!Settings.current.speakerMode) return;  //could happen just after turned off
    spk.setIcon(ii[state ? PIC_GREEN : PIC_RED]);
  }

  /** Change volume from slider control. */

  public void setVolRec() {
    sound.setVolRec(volRec.getValue());
  }

  /** Change volume from slider control. */

  public void setVolPlay() {
    sound.setVolPlay(volPlay.getValue());
  }

  public void setScales() {
    if (sound.isSWVolRec()) {
      micScale.setText("");
      micScale.setIcon(ii[PIC_SWSCALE]);
      volRec.setValue(Settings.current.volRecSW);
      sound.setVolRec(Settings.current.volRecSW);
    } else {
      micScale.setText("");
      micScale.setIcon(ii[PIC_HWSCALE]);
      volRec.setValue(Settings.current.volRecHW);
      sound.setVolRec(Settings.current.volRecHW);
    }
    if (sound.isSWVolPlay()) {
      spkScale.setText("");
      spkScale.setIcon(ii[PIC_SWSCALE]);
      volPlay.setValue(Settings.current.volPlaySW);
      sound.setVolPlay(Settings.current.volPlaySW);
    } else {
      spkScale.setText("");
      spkScale.setIcon(ii[PIC_HWSCALE]);
      volPlay.setValue(Settings.current.volPlayHW);
      sound.setVolPlay(Settings.current.volPlayHW);
    }
  }

  /** Select a new line. */

  public void selectLine(int newline) {
    if (line != -1 && lines[line].xfer) {
      for (int a=0;a<6;a++) lineButtons[a].setSelected(false);
      lineButtons[line].setSelected(true);
      if (newline == -1) {  //???
        return;
      }
      if (!lines[newline].talking) {
        return;
      }
      lines[newline].sip.referLive(lines[newline].callid, lines[line].callid);
      return;
    }
    if (line != -1 && Settings.current.autohold && lines[line].talking && !lines[line].hld && !lines[line].cnf && newline != line) {
      doHold();
    }
    //make sure line is valid
    for (int a=0;a<6;a++) lineButtons[a].setSelected(false);
    if ((line != -1) && (lines[line].dtmf != 'x')) lines[line].dtmfend = true;
    if (newline == -1) {
      line = -1;
      dial.setText("");
      status.setText("");
      hld.setIcon(ii[PIC_GREY]);
      dnd.setIcon(ii[PIC_GREY]);
      cnf.setIcon(ii[PIC_GREY]);
      videoPanel.setIcon(ii[PIC_GREY]);
      return;
    }
    lineButtons[newline].setSelected(true);
    sound.selectLine(newline);
    if (Settings.current.autohold && lines[line].talking && lines[line].hld == true && newline != line) {
      doHold();
    }
    line = newline;
    updateLine();
  }

  /** Update the number to be dialed, status and buttons for current selected line. */

  public void updateLine() {
    PhoneLine pl = lines[line];
    end.setSelected(false);
    if (pl.incall) {
      call.setSelected(true);
    } else {
      call.setSelected(false);
    }
    dial.setText(pl.dial);
    status.setText(pl.status);
    hld.setIcon(ii[pl.hld ? PIC_RED : PIC_GREY]);
    dnd.setIcon(ii[pl.dnd ? PIC_RED : PIC_GREY]);
    cnf.setIcon(ii[pl.cnf ? PIC_GREEN : PIC_GREY]);
    xfr.setIcon(ii[pl.xfer ? PIC_GREEN : PIC_GREY]);
    videoPanel.setIcon(ii[pl.videoWindow != null ? PIC_GREEN : PIC_GREY]);
  }

  public void updateCallButton(boolean state) {
    call.setSelected(state);
  }

  public void updateEndButton(boolean state) {
    end.setSelected(state);
  }

  public void endLineUpdate(int xline) {
    if ((Settings.current.usePublish) && (presenceStatus.getSelectedIndex() == 0)) lines[xline].sip.publish("open");
    if (lines[xline].videoWindow != null) {
      toggleVideoPanel(xline);
    }
    if (sound.record != null) {
      toggleRecord();
    }
  }

  public void callInviteUpdate() {
    updateRecentList();
    if ((Settings.current.usePublish) && (presenceStatus.getSelectedIndex() == 0)) lines[line].sip.publish("busy");
  }

/*
Line Colors:
  black=not registered
  grey=available
  green=in use
  red=unauth
  green/grey=ringing
*/

  /** TimerTask executed every 250ms to change the line lights(icons). */

  public class ButtonColorUpdater extends java.util.TimerTask {
    public boolean odd = false;
    public void run() {
      int clr = PIC_BLACK;
      int cnt = 0;
      for(int a=0;a<6;a++) {
        if (Settings.current.lines[a].user.length() > 0) cnt++;
        PhoneLine pl = lines[a];
        if ((pl.sip != null) && (pl.auth)) {
          clr = PIC_GREY;
          if (pl.msgwaiting) {
            if (odd) clr = PIC_ORANGE;
          }
          if (pl.incoming) {
            if (odd) clr = PIC_GREEN; else clr = PIC_GREY;
          } else if (pl.incall) clr = PIC_GREEN;
        } else {
          if (pl.unauth)
            clr = PIC_RED;
          else
            clr = PIC_BLACK;
        }
        if (pl.clr != clr) {
          lineButtons[a].setIcon(ii[clr]);
          pl.clr = clr;
        }
      }
      if (cnt == 0) {
        //let noob know to press the config button to get started
        if (odd) clr = PIC_RED; else clr = PIC_GREY;
        cfg.setIcon(ii[clr]);
      }
      else if (doConfig) {
        //let impatient user know the config screen will come up in a second
        if (odd) clr = PIC_GREEN; else clr = PIC_GREY;
        cfg.setIcon(ii[clr]);
      }
      odd = !odd;
    }
  }

  /** Sets a contacts icon status when received from a NOTIFY message. */

  public void setStatus(String number, String server, String status) {
    //BUG : Assuming port 5060 - TLS uses 5061
    if (server.indexOf(':') == -1) server += ":5060";
    JFLog.log("setStatus(" + number + "," + server + "," + status);
    for(int a=0;a<contactList.size();a++) {
      String contact = contactList.get(a).contact;
      String fields[] = SIP.split(contact);
      if (fields[2].indexOf(':') == -1) fields[2] += ":5060";
      if ( (fields[1].equals(number)) && (fields[2].equals(server)) ) {
        if (status.equals("open")) {
          contactList.get(a).setIcon(ii[PIC_ICON_OPEN]);
          break;
        }
        if (status.equals("closed")) {
          contactList.get(a).setIcon(ii[PIC_ICON_CLOSED]);
          break;
        }
        if (status.equals("idle")) {
          contactList.get(a).setIcon(ii[PIC_ICON_IDLE]);
          break;
        }
        if (status.equals("busy")) {
          contactList.get(a).setIcon(ii[PIC_ICON_BUSY]);
          break;
        }
        if (status.equals("dnd")) {
          contactList.get(a).setIcon(ii[PIC_ICON_DND]);
          break;
        }
        break;
      }
    }
    contactJList.repaint();
  }

  /** Updates contact list after a change. */

  public void updateContactList(boolean sub) {
    String fields1[], fields2[];
    //x = "display name" <sip:user@host;tag=...>;tag=...
    //return:    [0]          [1]  [2]   [...][:][...]
    for(int a=0;a<Settings.current.sipcontacts.length;a++) {
      fields1 = SIP.split(Settings.current.sipcontacts[a]);
      boolean ok = false;
      for(int b=0;b<contactList.size();b++) {
        fields2 = SIP.split(contactList.get(b).contact);
        if (fields1[0].equals(fields2[0])) {ok = true; break;}
      }
      if (!ok) {
        Contact contact = new Contact(fields1[0], Settings.current.sipcontacts[a]);
        contact.setIcon(ii[PIC_ICON_CLOSED]);
        contactList.add(contact);
        if (SIP.getFlag2(fields1, "monitor").equals("true")) addMonitor(Settings.current.sipcontacts[a], sub);
      }
    }
    contactJList.setListData(contactList.toArray());
  }

  /** Update recent lists after a change. */

  public void updateRecentList() {
    recentList.setListData(Settings.current.callLog);
  }

  /** Call a contact (double-click). */

  public void dialContact() {
    String contact = ((Contact)contactJList.getSelectedValue()).contact;
    String fields[] = SIP.split(contact);
    if (line == -1) return;
    if (lines[line].xfer) {
      lines[line].dial = fields[1];
      updateLine();
      return;
    }
    if (lines[line].incall) return;
    if (fields[1].length() == 0) return;
    lines[line].dial = fields[1];
    call();
  }

  /** Call a recent number (double-click). */

  public void dialRecent() {
    if (line == -1) return;
    String dial = (String)recentList.getSelectedValue();
    if ((dial == null) || (dial.length() == 0)) return;
    if (lines[line].xfer) {
      lines[line].dial = dial;
      updateLine();
      return;
    }
    if (lines[line].incall) return;
    lines[line].dial = dial;
    call();
  }

  /** Toggle side panel (contacts/recent lists). */

  public void toggleContacts() {
    showingContacts = !showingContacts;
    wc.setPanelSize();
    if (!showingContacts) {
      sidePanel.setText(">");
    } else {
      sidePanel.setText("<");
    }
  }

  public void toggleVideoPanel(int xline) {
    if (!JFNative.loaded) return;
    if (!Settings.current.nativeVideo) return;
    PhoneLine pl = lines[xline];
    if (pl.videoWindow == null) {
      if (xline == -1) {
        JFLog.log("Unable to start video : No line selected");
        return;
      }
      if (!pl.talking) {
        JFLog.log("Unable to start video : No call active");
        return;
      }
      SDP.Stream vstream = lines[xline].sdp.getFirstVideoStream();
      if (vstream == null) {
        JFLog.log("Unable to start video : No video stream");
        return;
      }
      if (vstream.codecs == null || vstream.codecs.length == 0 || getVideoCodec(xline) == null)
      {
        JFLog.log("Unable to start video: No video codec available");
        return;
      }
    }
    toggleVideoActive(xline);
    if (line == xline) updateLine();
  }

  private Codec getVideoCodec(int xline) {
    //returns first codec
    if (lines[xline].sdp == null) return null;
    SDP.Stream vstream = lines[xline].sdp.getFirstVideoStream();
    if (vstream == null) return null;
    if (vstream.codecs.length == 0) return null;
    Codec codec = vstream.codecs[0];
    if (LocalCamera.isRunning()) {
      if (!LocalCamera.getCodec().name.equals(codec.name)) return null;
    }
    return codec;
  }

  private void toggleVideoActive(int xline) {
    PhoneLine pl = lines[xline];
    if (pl.videoWindow == null) {
      Codec codec = getVideoCodec(xline);
      if (codec == null) return;
      RTP rtp = pl.videoRTP;
      rtp.start();  //may not have started yet
      pl.videoWindow = new VideoWindow(this, xline);
      if (!LocalCamera.isRunning()) {
        LocalCamera.setCodec(codec);
      }
      LocalCamera.enable(xline);
      for(int a=0;a<pl.sdp.streams.length;a++) {
        SDP.Stream stream = pl.sdp.streams[a];
        if (stream.type == SDP.Type.video) {
          if (stream.canRecv()) {
            RTPChannel channel = rtp.getDefaultChannel();
            if (channel == null || !channel.stream.content.equals(stream.content)) {
              channel = rtp.createChannel(stream);
              channel.start();
            }
            addRemoteCamera(pl, channel);
          }
        }
      }
    } else {
      LocalCamera.disable(xline);
      synchronized(pl.remoteCamerasLock) {
        for(int a=0;a<pl.remoteCameras.size();a++) {
          pl.remoteCameras.get(a).cancel();
        }
        pl.remoteCameras.clear();
      }
      pl.videoWindow.dispose();
      pl.videoWindow = null;
    }
  }

  /** Adds a new contact. */

  public void addContact() {
    String contact = EditContact.editContact(null, "\"New User\"<sip:@>", true);
    if (contact == null) {JFLog.log("Contact is null"); return;}
    String fields[] = SIP.split(contact);
    Settings.setContact(fields[0], contact);
    updateContactList(true);
    Settings.saveSettings();
  }

  /** Edits an existing contact. */

  public void editContact() {
    Contact label = (Contact)contactJList.getSelectedValue();
    if (label == null) return;
    String contact = label.contact;
    String orgcontact = contact;
    String orgfields[] = SIP.split(contact);
    String orgname = orgfields[0];
    contact = EditContact.editContact(null, contact, false);
    if (contact == null) return;
    String fields[] = SIP.split(contact);
    if (!fields[0].equals(orgname)) {
      Settings.delContact(orgname);
      if (SIP.getFlag2(orgfields, "monitor").equals("true")) delMonitor(orgcontact);
      for(int a=0;a<contactList.size();a++) {
        String fields2[] = SIP.split(contactList.get(a).contact);
        if (fields2[0].equals(orgname)) {
          contactList.remove(a);
          break;
        }
      }
    } else {
      if ((SIP.getFlag2(orgfields, "monitor").equals("true")) && (SIP.getFlag2(fields, "monitor").equals("false"))) delMonitor(orgcontact);
      for(int a=0;a<contactList.size();a++) {
        String fields2[] = SIP.split(contactList.get(a).contact);
        if (fields2[0].equals(fields[0])) {
          contactList.get(a).contact = contact;
          break;
        }
      }
    }
    Settings.setContact(fields[0], contact);
    updateContactList(true);
    Settings.saveSettings();
  }

  /** Deletes a contact. */

  public void delContact() {
    Contact label = (Contact)contactJList.getSelectedValue();
    if (label == null) return;
    String contact = label.contact;
    String fields[] = SIP.split(contact);
    if (JOptionPane.showConfirmDialog(this, "Delete " + fields[0] + "?", "Delete entry?", JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) return;
    Settings.delContact(fields[0]);
    for(int a=0;a<contactList.size();a++) {
      String fields2[] = SIP.split(contactList.get(a).contact);
      if (fields2[0].equals(fields[0])) {
        contactList.remove(a);
        break;
      }
    }
    updateContactList(true);
    Settings.saveSettings();
    if (SIP.getFlag2(fields, "monitor").equals("true")) delMonitor(contact);
  }

  public void hld_setIcon(ImageIcon ii) {
    hld.setIcon(ii);
  }

  public void aa_setIcon(ImageIcon ii) {
    aa.setIcon(ii);
  }

  public void ac_setIcon(ImageIcon ii) {
    ac.setIcon(ii);
  }

  public void dnd_setIcon(ImageIcon ii) {
    dnd.setIcon(ii);
  }

  public void cnf_setIcon(ImageIcon ii) {
    cnf.setIcon(ii);
  }

  public void mute_setIcon(ImageIcon ii) {
    mute.setIcon(ii);
  }

  public void spk_setIcon(ImageIcon ii) {
    spk.setIcon(ii);
  }

  public void setFonts() {
//    JFLog.log("Font name= " + l1.getFont().getName() + ",stype=" + l1.getFont().getStyle() + ",size=" + l1.getFont().getSize() );
    //sets font sizes for all buttons
    int small = 10;
    int large = 12;  //default size
    if (Settings.current.smallerFont) {
      small -= 2;
      large -= 2;
    }
    Font smallFont = new java.awt.Font("Dialog", 0, small);
    Font largeFont = new java.awt.Font("Dialog", 0, large);

    //these components use three or more letter and require a smaller font
    clear.setFont(smallFont);
    //the remaining components are okay with the normal font
    sidePanel.setFont(largeFont);
    addContact.setFont(largeFont);
    editContact.setFont(largeFont);
    delContact.setFont(largeFont);
    contactLabel.setFont(largeFont);
    recentLabel.setFont(largeFont);
  }

  public void setPresenceStatus() {
    presenceStatus.setSelectedIndex(0);
    if (Settings.current.usePublish) {
      presenceStatus.setEnabled(true);
    } else {
      presenceStatus.setEnabled(false);
    }
  }

  public void publishPresence() {
    if (line == -1) return;
    String state = (String)presenceStatus.getSelectedItem();
    if (state.equals("Auto")) {
      if (lines[line].incall) state = "busy"; else state = "open";
    } else if (state.equals("DND")) {
      state = "dnd";
    } else if (state.equals("Busy")) {
      state = "busy";
    } else if (state.equals("Idle")) {
      state = "idle";
    } else if (state.equals("Offline")) {
      state = "closed";
    }
    for(int a=0;a<6;a++) {
      if (Settings.current.lines[a].same != -1) continue;
      if (lines[a].sip == null) continue;
      if (!lines[a].sip.isRegistered()) continue;
      lines[a].sip.publish(state);
    }
  }

  //NOTE:using "presence" here, could also use "reg" to track registration

  public void addMonitor(String contact, boolean sub) {
//    JFLog.log("addMonitor:" + contact);
    String fields[] = SIP.split(contact);
    //only want first 3 fields in the monitorList
    String fields3[] = new String[3];
    fields3[0] = fields[0];
    fields3[1] = fields[1];
    fields3[2] = fields[2];
    String contact3 = SIP.join(fields3);
    monitorList.add(contact3);
    if (!sub) return;
    //we need to see if a SIP is already registered that we can subscribe to
    for(int a=0;a<6;a++) {
      if (lines[a].sip == null) continue;
      if (!lines[a].sip.isRegistered()) continue;
      if (lines[a].sip.getRemoteHost().equals(fields[3])) {
        //found one that matches
        lines[a].sip.subscribe(fields3[1], "presence", 3600);
        return;
      }
    }
  }

  public void delMonitor(String contact) {
    String fields[] = SIP.split(contact);
    String fields3[] = new String[3];
    fields3[0] = fields[0];
    fields3[1] = fields[1];
    fields3[2] = fields[2];
    String contact3 = SIP.join(fields3);
    for(int a=0;a<monitorList.size();a++) {
      if (monitorList.get(a).equals(contact3)) {
        monitorList.remove(a);
        break;
      }
    }
    //we need to see if a SIP is already registered that we can subscribe to
    for(int a=0;a<6;a++) {
      if (lines[a].sip == null) continue;
      if (!lines[a].sip.isRegistered()) continue;
      if (lines[a].sip.getRemoteHost().equals(fields[3])) {
        //found one that matches
        lines[a].sip.subscribe(fields3[1], "presence", 0);
        return;
      }
    }
  }

  public void clearMonitor() {
    monitorList = new Vector<String>();
  }

  public void monitorSubscribe(SIPClient sip) {
    //subscribe for any that belong to sip
    String server = sip.getRemoteHost();
    for(int a=0;a<monitorList.size();a++) {
      String contact = monitorList.get(a);
      String fields[] = SIP.split(contact);
      if (fields[2].equalsIgnoreCase(server)) {
        sip.subscribe(fields[1], "presence", 3600);
      }
    }
  }

  public void onRegister(SIPClient sip) {
    if ((Settings.current.usePublish) && (presenceStatus.getSelectedIndex() == 0)) lines[line].sip.publish("open");
    monitorSubscribe(sip);
  }

  public Dimension getPreferredSize() {
    int x = 290;
    int y = 360;
    if (showingContacts) x += 270;
    return new Dimension(x, y);
  }

  //NOTE : H.263 Valid sizes are 128x96, 176x144, 352x288, 704x576, and 1408x1152
  public static final int vx = 352;
  public static final int vy = 288;

  public void rtp_jpeg_receive(RTPChannel channel, byte data[], int pos, int len) {
    RemoteCamera camera = findRemoteCamera(channel);
    if (camera != null) camera.rtp_jpeg_data(data, pos, len);
  }
  public void rtp_h263_receive(RTPChannel channel, byte data[], int pos, int len) {
    RemoteCamera camera = findRemoteCamera(channel);
    if (camera != null) camera.rtp_h263_data(data, pos, len);
  }
  public void rtp_h263_1998_receive(RTPChannel channel, byte data[], int pos, int len) {
    RemoteCamera camera = findRemoteCamera(channel);
    if (camera != null) camera.rtp_h263_1998_data(data, pos, len);
  }
  public void rtp_h263_2000_receive(RTPChannel channel, byte data[], int pos, int len) {
    RemoteCamera camera = findRemoteCamera(channel);
    if (camera != null) camera.rtp_h263_2000_data(data, pos, len);
  }
  public void rtp_h264_receive(RTPChannel channel, byte data[], int pos, int len) {
    RemoteCamera camera = findRemoteCamera(channel);
    if (camera != null) camera.rtp_h264_data(data, pos, len);
  }
  public void rtp_vp8_receive(RTPChannel channel, byte data[], int pos, int len) {
    RemoteCamera camera = findRemoteCamera(channel);
    if (camera != null) camera.rtp_vp8_data(data, pos, len);
  }

  public String getLineStatus() {
    return status.getText();
  }

  public void toggleRecord() {
    if (line == -1) return;
    if (sound.record == null) {
      Record record = new Record();
      record.open();
      sound.record = record;
    } else {
      Record record = sound.record;
      sound.record = null;
      JF.sleep(40);
      record.close();
    }
    record.setIcon(ii[sound.record != null ? PIC_RED : PIC_GREY]);
  }

  private int find_best_length(byte data[], int offset, int length) {
    //see if there is a 0,0,1 and return a length to that
    //this way the next packet will start at a resync point
    int a = 1;
    while (data[offset + a] == 0) a++;  //skip the one at the beginning of this packet
    for(;a<length-3;a++) {
      if (data[offset + a] == 0 && data[offset + a + 1] == 0 && data[offset + a + 2] == 1) return a;
    }
    return length;
  }

  private void printArray(String msg, byte data[], int offset, int datalen) {
    StringBuilder sb = new StringBuilder();
    sb.append(msg);
    sb.append(",len=");
    sb.append(Integer.toString(datalen));
    sb.append('=');
    while (datalen > 0) {
      int packlen = datalen;
      packlen = find_best_length(data, offset, packlen);
      int printlen = packlen;
      if (printlen > 16) printlen = 16;
      sb.append(";packlen=" + packlen + "=");
      for(int a=0;a<printlen;a++) {
        if (a > 0) sb.append(',');
        int b = ((int)data[offset + a]) & 0xff;
        if (b < 16) sb.append('0');
        sb.append(Integer.toString(b, 16));
      }
      datalen -= packlen;
      offset += packlen;
    }
    JFLog.log(sb.toString());
  }

  private boolean validSIPRange() {
    if (Settings.current.sipmin <= 0) return false;
    if (Settings.current.sipmax <= 0) return false;
    if (Settings.current.sipmax < Settings.current.sipmin) return false;
    if (Settings.current.sipmax - Settings.current.sipmin < 32) return false;
    return true;
  }

  private boolean validRTPRange() {
    if (Settings.current.rtpmin <= 0) return false;
    if (Settings.current.rtpmax <= 0) return false;
    if (Settings.current.rtpmax < Settings.current.rtpmin) return false;
    if (Settings.current.rtpmax - Settings.current.rtpmin < 128) return false;
    return true;
  }

  private void setPortRanges() {
    if (Settings.current.sipRange && validSIPRange()) {
      this.setSIPPortRange(Settings.current.sipmin, Settings.current.sipmax);
    } else {
      this.setSIPPortRange(5061, 5199);
    }
    if (Settings.current.rtpRange && validRTPRange()) {
      RTP.setPortRange(Settings.current.rtpmin, Settings.current.rtpmax);
    } else {
      RTP.setPortRange(32768, 65534);
    }
  }

  private void setNAT() {
    SIPClient.NAT nat = null;
    switch (Settings.current.nat) {
      case 0: nat = SIPClient.NAT.None; break;
      case 2: nat = SIPClient.NAT.STUN; break;
      case 3: nat = SIPClient.NAT.TURN; break;
      case 4: nat = SIPClient.NAT.ICE; break;
    }
    SIPClient.setNAT(nat, Settings.current.natHost, Settings.current.natUser, Settings.current.natPass);
    SIPClient.useNATOnPrivateNetwork(Settings.current.natPrivate);
    SIPClient.setEnableRport(Settings.current.rport);
    SIPClient.setEnableReceived(Settings.current.received);
    if (Settings.current.nat >= 3) {
      RTP.enableTURN(Settings.current.natHost, Settings.current.natUser, Settings.current.natPass);
    } else {
      RTP.disableTURN();
    }
  }

  private void reset() {
    setPortRanges();
    setNAT();
    setFonts();
    JFLog.setEnabled(!Settings.current.disableLogging);
    wc.setPanelAlwaysOnTop(Settings.current.alwaysOnTop);
    updateContactList(false);
    updateRecentList();
    if (!sound.init(lines, this)) {
      JOptionPane.showMessageDialog(this,
        "No compatible sound found",
        "Warning",
        JOptionPane.INFORMATION_MESSAGE);
    }
    setPresenceStatus();
    setScales();
    new Thread() {
      public void run() {
        reRegisterAll();
      }
    }.start();
    if ((!Settings.isLinux) && (!Settings.isWindows)) {
      videoPanel.setEnabled(false);
    } else {
      videoPanel.setEnabled(true);
    }
  }

  public void doConfig() {
    cfg.doClick();
  }
}
